package com.netty.java.nio.groupchat;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;

/**
 * @Author: TongRui乀
 * @Date: 2020-05-04 8:46
 * @description： 群聊服务端
 */
@Slf4j
public class GroupChatServer {

    /**
     * 选择器
     */
    private Selector selector;
    /**
     * 服务端channel
     */
    private ServerSocketChannel serverSocketChannel;
    /**
     * 服务端端口
     */
    private static final int PORT = 8888;

    /**
     * 在构造方法中启动server
     */
    public GroupChatServer() {

        try {
            // 实例selector
            selector = Selector.open();
            // 实例channel
            serverSocketChannel = ServerSocketChannel.open();
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(PORT));
            // 设置非阻塞
            serverSocketChannel.configureBlocking(false);
            // 注册到selector 并绑定接收链接事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            log.info("server start success");
        } catch (IOException e) {
            log.error("server 启动失败：{}", e.getMessage());
        }
    }

    /**
     * 处理客户端连接请求
     */
    public void accept() {

        try {
            // 循环处理连接
            while (true) {

                // 延迟判断客户端channel事件
                if (selector.select(2000) < 1) {
                    continue;
                }
                // 获取有事件的selectionKey
                Set<SelectionKey> keys = selector.selectedKeys();
                // 处理有事件的key  todo 要不要删掉已经处理完的key
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()){
                    handleSelectionKey(iterator.next());
                }
                iterator.remove();
            }
        } catch (IOException e) {
            log.error("客户端连接失败");
        }


    }

    /**
     * 处理有事件的key
     *
     * @param key
     */
    private void handleSelectionKey(SelectionKey key) {

        SocketChannel channel = null;

        try {
            // 连接请求
            if (key.isAcceptable()) {

                // 获取通道
                channel = serverSocketChannel.accept();
                // 设置非阻塞
                channel.configureBlocking(false);
                // 注册到selector 并绑定读取事件
                channel.register(selector, SelectionKey.OP_READ);

                log.info("客户端：{} 上线", channel.getRemoteAddress());
            } else if (key.isReadable()) { // 读通道

                readAndForwardMessage(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("用户下线");
        }
    }

    /**
     * 接收消息并转发
     *
     * @param key
     */
    private void readAndForwardMessage(SelectionKey key) throws IOException {

        SocketChannel channel = null;

        try {
            channel = (SocketChannel) key.channel();

            ByteBuffer readBuffer = ByteBuffer.allocate(1024);

            int read = channel.read(readBuffer);


            if (read > 0) {
                //
                String message = new String(Arrays.copyOfRange(readBuffer.array(), 0, read));

                log.info("服务端接收到消息:{}", message);

                // 转发消息
                forwardMessage(channel, message);
            }
        } catch (IOException e) {
            log.error("接收转发消息失败：{} ", e.getMessage());
            log.info("用户:{} 下线", channel.getRemoteAddress());
            // 取消注册
            key.cancel();
            // 关闭通道
            channel.close();
        }

    }

    /**
     * 转发消息
     *
     * @param channel
     * @param message
     */
    private void forwardMessage(SocketChannel channel, String message) {

        // 所有的注册的SelectionKey
        Set<SelectionKey> selectionKeys = selector.keys();
        // 遍历转发消息
        selectionKeys.forEach(key -> {

            try {
                Channel sc = key.channel();

                if (sc instanceof SocketChannel && sc != channel) {

                    SocketChannel target = (SocketChannel) sc;

                    target.write(ByteBuffer.wrap(message.getBytes()));

                    log.info("{}：消息转发成功", target.getRemoteAddress());
                }
            } catch (IOException e) {
                log.error("转发消息失败:{}", e.getMessage());
            }

        });
    }

    public static void main(String[] args) {

        GroupChatServer server = new GroupChatServer();

        server.accept();

    }


}
